package com.longtop.framework.base;

import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.longtop.framework.common.CacheUtil;
import com.longtop.framework.common.Constant;
import com.longtop.framework.security.BaseUncheckedException;
import com.longtop.framework.security.SessionObject;
import com.longtop.framework.sys.codeInfo.model.TSysCodeInfo;
import com.longtop.framework.util.SpringBeanFactory;
import com.longtop.framework.util.StringUtil;
import com.longtop.intelliweb.action.AbstractBaseAction;
import com.longtop.intelliweb.action.IActionContext;
import com.longtop.intelliweb.action.IInitContext;
import com.longtop.intelliweb.common.Constants;
import com.longtop.intelliweb.dataengine.DataEngineException;
import com.longtop.intelliweb.view.AbstractControl;
import com.longtop.intelliweb.view.AbstractVisibleControl;
import com.longtop.intelliweb.view.Dataset;
import com.longtop.intelliweb.view.Forward;
import com.longtop.intelliweb.view.ListDropDown;
import com.longtop.intelliweb.view.ListDropDownItem;
import com.longtop.intelliweb.view.RecordButton;
import com.longtop.intelliweb.view.RecordNavigator;

/**
 * 系统所有需要验证session的Action类的父类，用于对客户端提交的请求做统一处理。<br>
 * 如：用户权限验证等。<br>
 * 
 * @author 胡砥峰
 * 
 */
public class BaseAction extends AbstractBaseAction {

	/**
	 * 作用：操作权限过滤权限、初始化页面数据
	 * 
	 * @author 胡砥峰
	 * 
	 * @param context
	 */
	public void init(IInitContext context) {
		try {
			this.beforeInit(context);
			this.thisPageInit(context);
		} catch (Exception e) {
			e.printStackTrace();
			try {
				context.getResponse().sendRedirect(context.getRequest().getContextPath() + "/uncaughtException.jsp");
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}

	/**
	 * 作用：执行子类方法，分发请求
	 * 
	 * @author 胡砥峰
	 */
	public void dispatch(IActionContext context) throws DataEngineException, InvocationTargetException {

		SessionObject sessionObject = this.getSessionObject(context);
		// 如果session过期、或者未登录，转向登录主页面
		if (sessionObject == null) {
			Forward forward = new Forward();
			forward.setPath("/index.jsp");
			forward.setTarget("_blank");
			context.setForward(forward);
		} else {
			try {
				super.dispatch(context);
			} catch (Exception e) {
				log.logError(e.getMessage(), e);
				try {
					// context.getResponse().sendRedirect(context.getRequest().getContextPath()+"/uncaughtException.jsp");
					Forward forward = new Forward();
					forward.setPath("/uncaughtException.jsp");
					forward.setTarget("_blank");
					context.setForward(forward);
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}
	}

	/**
	 * 由子类覆写实现，用于页面 业务初始化工作
	 * 
	 * @param context
	 */
	public void thisPageInit(IInitContext context) {

	}

	/**
	 * 作用：每个jsp页面初始化之前,执行这个, 控制button操作权限
	 * 
	 * @author 胡砥峰
	 * 
	 * @param context
	 */
	private void beforeInit(IInitContext context) {

		SessionObject sessionObject = (SessionObject) context.getRequest().getSession().getAttribute(com.longtop.framework.common.Constant.SESSION_OBJECT);

		// 如果session过期、或者未登录，转向登录主页面
		if (sessionObject == null) {
			try {
				context.getResponse().sendRedirect(context.getRequest().getContextPath() + "/index.jsp");
				return;
			} catch (Exception e) {
				e.printStackTrace();
			}
			return;
		}

		// 取得页面的所有控件
		List list = context.getControls();

		AbstractControl control = null;
		RecordNavigator recordNavigator = null;
		List listButton = null;
		RecordButton control2 = null;

		// 校验 普通button控件，导航控件中包含的button需要特殊处理一下。
		for (int i = 0; list != null && i < list.size(); i++) {
			control = (AbstractControl) list.get(i);

			// 自动加载 ListDropDown下拉框的数据
			if (control.getControlType().equals(Constants.CONTROL_LISTDROPDOWN)) {
				ListDropDown listDropDown = (ListDropDown) control;
				String listDropDownId = StringUtil.getString(listDropDown.getId());

				// 下拉框id不为空，取 (两个)下划线 后面的几位，就是codeType的编码
				String codeTypeId = StringUtils.substringAfterLast(listDropDownId, "__");
				if (StringUtils.isNotEmpty(codeTypeId)) {
					this.fillListDropDownByCodeTypeId(listDropDown, codeTypeId);
				}
			}

			// 如果是RecordNavigator，获取该控件中的每个RecordButton进行校验. String
			if (control.isVisibleControl() && control.getControlType().equals(Constants.CONTROL_RECORDNAVIGATOR)) {
				recordNavigator = (RecordNavigator) control;
				listButton = recordNavigator.getRecordButtons();
				boolean flag = false; // 标记，
				for (int j = 0; listButton != null && j < listButton.size(); j++) {
					control2 = (RecordButton) listButton.get(j);
					if (flag) {
						setComponentAuth(control2, context); // 如果flag为true，表示这个导航条已经有button需要显示
					} else {
						flag = setComponentAuth(control2, context); // 如果flag为false，表示这个导航条目前还没有有button需要显示
					}
				}
				if (!flag)
					recordNavigator.setVisible("false"); // 如果flag为false，表示这个导航条目前还没有有button需要显示,所以将导航条隐藏
			} else if (control.isVisibleControl()) {// 其他可视控件校验
				setComponentAuth((AbstractVisibleControl) control, context);
			}
		}
	}

	/**
	 * 作用：验证 导航RecordNavigator控件中的 button 是否可视、当前操作人是否有权限可操作 对所有可视控件关联权限码进行是否可视(visible)、<br>
	 * 是否可操作(enabled)的校验。
	 * 
	 * @author 胡砥峰
	 * 
	 * @param control
	 *            RecordButton控件
	 * @param context
	 *            上下文
	 * 
	 * @return true 表示控件可使， false 表示控件不可视
	 * @改造说明：胡砥峰（2009-08-25） 解决导航条上的按钮都不能操作的时候，还会出现一个导航条的形状
	 */
	private boolean setComponentAuth(RecordButton control, IInitContext context) {
		// 从控件的visible属性中读取该控件的权限码
		String visibleString = control.getVisible();

		// 从控件的enabled属性中读取该控件的权限码
		String enabledString = control.getEnabled();

		boolean visible = true;
		boolean enabled = true;

		// 1.首先判断是否可视(如果不可视，则不判断enabled)
		if (visibleString.equals("false")) {
			visible = false;
		} else if (visibleString.equals("true") || visibleString.equals("")) {
			visible = true;
		} else {
			visible = hasRight(context, visibleString);
		}
		control.setVisible(String.valueOf(visible));

		// control不可视，不用校验了，return
		if (!visible) {
			return false;
		}

		// 2.最后判断是否可操作enabled
		if (enabledString.equals("false")) {
			enabled = false;
		} else if (enabledString.equals("true") || enabledString.equals("")) {
			enabled = true;
		} else {
			enabled = hasRight(context, enabledString);
		}

		// 胡砥峰修改，将不能操作的按钮隐藏! 2009-08-06
		if (enabled) {
			control.setEnabled(String.valueOf(enabled));
			return true;
		} else {
			control.setVisible("false");
			return false;
		}
	}

	/**
	 * 作用：验证 button控件 是否可视、当前操作人是否有权限可操作 对所有可视控件关联权限码进行是否可视(visible)、<br>
	 * 是否可操作(enabled)的校验。
	 * 
	 * @author 胡砥峰
	 * 
	 * @param control
	 *            可视控件
	 * @param context
	 *            上下文
	 */
	private void setComponentAuth(AbstractVisibleControl control, IInitContext context) {
		// 从控件的visible属性中读取该控件的权限码
		String visibleString = control.getVisible();

		// 从控件的enabled属性中读取该控件的权限码
		String enabledString = control.getEnabled();
		boolean visible = true;
		boolean enabled = true;

		// 1.首先判断是否可视(如果不可视，则不判断readOnly及enabled)
		if (visibleString.equals("false")) {
			visible = false;
		} else if (visibleString.equals("true") || visibleString.equals("")) {
			visible = true;
		} else {
			visible = hasRight(context, visibleString);
		}
		control.setVisible(String.valueOf(visible));
		// 流程控制前台按钮状态需要使用下面的tag。以前用于流程控制，暂时屏蔽。modified by wangbinbin at
		// 2009-08-11 14:02
		// control.setTag(String.valueOf(visible));
		if (!visible) {
			return;
		}

		// 3.最后判断是否可操作
		if (enabledString.equals("false")) {
			enabled = false;
		} else if (enabledString.equals("true") || enabledString.equals("")) {
			enabled = true;
		} else {
			enabled = hasRight(context, enabledString);
		}

		// 胡砥峰修改，将不能操作的按钮隐藏! 2009-08-06
		if (enabled)
			control.setEnabled(String.valueOf(enabled));
		else {
			if (!(Constants.CONTROL_CHECKBOX).equals(control.getControlType())) {
				control.setStyle("display:none");
			}
		}
	}

	/**
	 * 作用：根据传入的权限码判断是否存在用户session的权限串中
	 * 
	 * @author 胡砥峰
	 * 
	 * @param context
	 *            intelliWeb Web层的上下文
	 * @param operId
	 *            权限码
	 * @return boolean true:有权限，false:没有权限
	 */
	private boolean hasRight(IInitContext context, String operId) {
		SessionObject sessionObject = this.getSessionObject(context);
		List authOperFuncList = sessionObject.getAuthOperFuncList();
		boolean hasAuthRight = false;
		if (sessionObject != null) {
			hasAuthRight = (authOperFuncList != null && authOperFuncList.contains(operId));
		}

		return hasAuthRight;
	}

	/**
	 * 作用：从session中获取 用户信息对象
	 * 
	 * @author 胡砥峰
	 * 
	 * @param context
	 *            IActionContext或者IInitContext
	 * @return SessionObject
	 */
	public SessionObject getSessionObject(Object object) {
		if (object instanceof IActionContext) {
			IActionContext context = (IActionContext) object;

			SessionObject sessionObject = (SessionObject) context.getRequest().getSession().getAttribute(Constant.SESSION_OBJECT);

			// 如果session过期、或者未登录，转向登录主页面
			if (sessionObject == null) {
				try {
					// context.getResponse().sendRedirect(context.getRequest().getContextPath()+"/index.jsp");
					Forward forward = new Forward();
					forward.setId("index");
					forward.setPath("/index.jsp");
					forward.setTarget("_blank");
					context.setForward(forward);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return sessionObject;
		} else {
			IInitContext context = (IInitContext) object;

			SessionObject sessionObject = (SessionObject) context.getRequest().getSession().getAttribute(Constant.SESSION_OBJECT);

			// 如果session过期、或者未登录，转向登录主页面
			if (sessionObject == null) {
				try {
					context.getResponse().sendRedirect(context.getRequest().getContextPath() + "/index.jsp");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return sessionObject;
		}
	}

	// *****************************【胡砥峰提示】分割线：以上是校验系统权限必不可少的代码，以下是方便开发总结的公用方法*****************************//

	/**
	 * 根据beanId获取spring bean实例 <br/>
	 * 
	 * @author 胡砥峰
	 * 
	 * @param beanId
	 * @return
	 */
	public <T> T getBean(String beanId) {
		return SpringBeanFactory.getBean(beanId);
	}

	/**
	 * 获取jsp页面上的 dataset
	 * 
	 * @author 胡砥峰
	 * 
	 * @param object
	 * @param datasetName
	 * @return
	 */
	public Dataset getDataset(Object object, String datasetName) {
		if (object instanceof IActionContext) {
			IActionContext context = (IActionContext) object;
			return context.getDataset(datasetName);
		} else {
			IInitContext context = (IInitContext) object;
			return context.getDataset(datasetName);
		}
	}

	/**
	 * 根据代码类别ID填充下拉框的值
	 * 
	 * @author 胡砥峰
	 * 
	 * @param listDropDown
	 * @param codeTypeId
	 */
	public void fillListDropDownByCodeTypeId(ListDropDown listDropDown, String codeTypeId) {

		// 读取缓存
		List<TSysCodeInfo> list = CacheUtil.getCodeInfoListByCodeTypeId(codeTypeId);

		if (list.size() > 0) {
			for (Iterator iterator = list.iterator(); iterator.hasNext();) {
				TSysCodeInfo tSysCodeInfo = (TSysCodeInfo) iterator.next();

				ListDropDownItem listDropDownItem = new ListDropDownItem();
				listDropDownItem.setLabel(tSysCodeInfo.getContent());
				listDropDownItem.setName(tSysCodeInfo.getCodeInfoId());
				listDropDownItem.setValue(tSysCodeInfo.getValue());
				listDropDown.addItem(listDropDownItem);
			}
		}
	}

	/**
	 * 获取jsp页面上的 参数
	 * 
	 * @author 胡砥峰
	 * 
	 * @param object
	 * @param paramName
	 * @return
	 */
	public String getParameter(Object object, String paramName) {
		if (object instanceof IActionContext) {
			IActionContext context = (IActionContext) object;
			return StringUtil.getString(context.getCommandParameterValue(paramName));
		} else if (object instanceof IInitContext) {
			IInitContext context = (IInitContext) object;
			return StringUtil.getString(context.getRequest().getParameter(paramName));
		} else {
			throw new BaseUncheckedException(null, "不能获取参数值");
		}
	}

	/**
	 * 获取页面上text传入的值，用于自定义查询组装参数
	 * 
	 * @param context:IActionContext
	 * @return 返回以文本框id为key,文本框值为value的Map
	 */
	public Map<String, Object> getElementsToMap(Object obj) {
		Map<String, Object> map = new HashMap<String, Object>();
		if (obj instanceof IActionContext) {
			IActionContext context = (IActionContext) obj;
			Enumeration enum1 = context.getElementIds();
			String elementID = null;
			while (enum1.hasMoreElements()) {
				elementID = (String) enum1.nextElement();
				String mapId = StringUtil.trimLeft(elementID.replace("Text_", "")).trim();
				String mapValue = StringUtil.trimLeft(context.getElementValue(elementID)).trim();
				map.put(mapId, mapValue);
			}
		}
		return map;
	}
}
